#pragma once

#include <boost/signals2.hpp>
#include <boost/unordered_map.hpp>
#include <boostext/util/weak_ptr.hpp>
#include <boostext/util/make_shared.hpp>

#include <QtCore/QStack>
#include <QtCore/QSet>

#include "State.hpp"

#pragma warning (push)
#pragma warning (disable: 4251)
#pragma warning (disable: 4275)




namespace Engine
{
	namespace Ui
	{
		class ENGINE_UI_API SystemState
			: public State
		{
		public:

			SHARED(SystemState);
			typedef boost::signals2::connection                                                        connection;
			typedef std::stack<std::pair<boost::weak_ptr<Widget>,connection>>                          WidgetStack;
			typedef boost::unordered_map<boost::weak_ptr<Widget>,connection>                           WidgetSet;
			typedef boost::unordered_map<boost::weak_ptr<Widget>,std::pair<Border::Type,connection>>   WidgetBorders;

		protected:

			/**
			 * Create a SystemState.
			 */
			SystemState(const Cursor& cursor, boost::weak_ptr<Widget> modalWidget, const int2& parentPos = int2(0, 0));

			virtual void post_create(ptr _this);

		public:

			SHARED(SystemState);
			MAKE_SHARED2(SystemState, Cursor, boost::weak_ptr<Widget>);
			MAKE_SHARED3(SystemState, Cursor, boost::weak_ptr<Widget>, int2);

			/**
			 * Destroy a SystemState.
			 */
			virtual ~SystemState();



			/**
			 * Get the currently active modal widget.
			 * Usually it's the desktop, unless there is another
			 * modal widget.
			 */
			boost::shared_ptr<Widget> modalWidget() const;

			/**
			 * Set the currently active modal widget.
			 */
			void setModalWidget(boost::weak_ptr<Widget> modalWidget);



			/**
			 * Get the current widget with focus.
			 */
			boost::shared_ptr<Widget> focusWidget() const;

			/**
			 * Set the currently active focus widget.
			 */
			void setFocusWidget(boost::weak_ptr<Widget> widget);

			/**
			 * Unset the currently active focus widget.
			 */
			void unsetFocusWidget(boost::weak_ptr<Widget> widget);



			/**
			 * Get the cursor.
			 */
			Cursor& cursor();

			/**
			 * Get the cursor.
			 */
			const Cursor& cursor() const;

			/**
			 * Get the parent position for this widget.
			 * Usually a widget modifies this position, to include
			 * it's own parent position, when handing a NestedState to a child.
			 */
			int2 parentPos() const;

			/**
			 * Attach a widgets position to the mouse ones.
			 * As long as the widget is attached to the cursor,
			 * it will move with it.
			 */
			void attach(boost::weak_ptr<Widget> widget);
			void detach(boost::weak_ptr<Widget> widget);
			bool isAttached(boost::weak_ptr<Widget> widget) const;

			/**
			 * Attach a widgets border to the mouse.
			 * This will cause the widget to resize when 
			 * the mouse moves.
			 */
			void attachBorder(boost::weak_ptr<Widget> widget, Border::Type border);
			void detachBorder(boost::weak_ptr<Widget> widget);
			bool isAttachedBorder(boost::weak_ptr<Widget> widget) const;

			/**
			 * Test if anything is attached to the mouse.
			 */
			bool hasAttached() const;

			void moveAttached() const;
			void resizeAttached() const;

		private:

			void widgetDestroyed(boost::weak_ptr<Widget> widget);
			void windowDestroyed(boost::weak_ptr<Widget> widget);
			void modalWidgetDestroyed(boost::weak_ptr<Widget> widget);
			void focusWidgetDestroyed(boost::weak_ptr<Widget> widget);

		private:

			/// Weak pointer to this instance
			weak_ptr        m_this;

			/// The cursor of the GUI
			Cursor          m_cursor;

			/// The currently modal widget, usually the desktop, unless there is a modal dialog
			WidgetStack     m_modalWidgets;

			/// The currently focused widget, may be empty as well
			WidgetStack     m_focusWidgets;

			/// The screen space position (relative to the upper left corner of the window)
			/// of the parent (0, 0) for the desktop window. Shifted to the lower right for
			/// child-widgets.
			int2            m_parentPos;

			WidgetSet       m_attachedWidgets;
			WidgetBorders   m_attachedBorders;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////

#pragma warning (push)
